/*
 * Copyright (c) 2013, SRI International
 * All rights reserved.
 * Licensed under the The BSD 3-Clause License;
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 * 
 * http://opensource.org/licenses/BSD-3-Clause
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the aic-web-praise nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.sri.ai.lpitools.gwtgui.client;

import java.util.Date;

import com.google.gwt.activity.shared.ActivityManager;
import com.google.gwt.activity.shared.ActivityMapper;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.place.shared.PlaceHistoryHandler;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimpleLayoutPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.web.bindery.event.shared.EventBus;
import com.sri.ai.lpitools.cobra.gwtutil.dataobjects.shared.UserInfo;
import com.sri.ai.lpitools.gwtgui.client.components.dialogs.MessageDialog;
import com.sri.ai.lpitools.gwtgui.client.components.manager.AICManager;
import com.sri.ai.lpitools.gwtgui.client.components.manager.ExecutionEngineDistributor;
import com.sri.ai.lpitools.gwtgui.client.components.manager.ModelManager;
import com.sri.ai.lpitools.gwtgui.client.components.manager.UserListener;
import com.sri.ai.lpitools.gwtgui.client.components.manager.UserManager;
import com.sri.ai.lpitools.gwtgui.client.components.querypanel.EvidenceDataManager;
import com.sri.ai.lpitools.gwtgui.client.components.querypanel.QueryDataManager;
import com.sri.ai.lpitools.gwtgui.client.components.querypanel.QueryOptionsManager;
import com.sri.ai.lpitools.gwtgui.client.mappers.AppActivityMapper;
import com.sri.ai.lpitools.gwtgui.client.mappers.AppPlaceHistoryMapper;
import com.sri.ai.lpitools.gwtgui.client.place.MainPagePlace;
import com.sri.ai.lpitools.gwtgui.client.util.JSONUtil;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class AICGui implements EntryPoint {
	private static final String COOKIE_ID="AIC_Web_SID";
	
	private Place defaultPlace = new MainPagePlace();
    private SimpleLayoutPanel appWidget = new SimpleLayoutPanel();
    
	public void onModuleLoad() {
		
		//check to see if there is a browser cookie with a set sessionID
    	String sessionID = Cookies.getCookie(COOKIE_ID);
    	if(sessionID != null){
    		//if a cookie exists then check that the sessionId is still valid
    		checkHttpSession(sessionID);
    	} else {
    		//if there is no cookie prompt the user to login
    		showLogin();
    	}
    }
	
	/**
	 * This is the function that displays the login option to the user
	 */
	protected void showLogin(){
    	Window.setTitle("Please Login");
    	FlexTable main = new FlexTable();
		//create the user textbox and the password textbox and the labels for both
		final TextBox user = new TextBox();
		
		final TextBox password = new TextBox();
		password.setText("<not required>");
		password.setReadOnly(true);
		
		user.addStyleName("aic-LogInBox");
		password.addStyleName("aic-LogInBox");
		Label userLabel = new Label("UserName: ");
		Label passLabel = new Label("Password: ");
		Button login = new Button("Login");
		
		FlexTable loginTable = new FlexTable();
		FlexCellFormatter fcf = loginTable.getFlexCellFormatter();
		loginTable.setCellPadding(3);
		HTML label = new HTML("Please log in");
		label.setStyleName("aic-htmlLabel");
		loginTable.setWidget(0, 0, label);
		fcf.setColSpan(0,0,2);
		fcf.setHorizontalAlignment(0,0,HasHorizontalAlignment.ALIGN_CENTER);
		
		loginTable.setWidget(1, 0, userLabel);
		loginTable.setWidget(2, 0, passLabel);
		fcf.setHorizontalAlignment(1, 0, HasHorizontalAlignment.ALIGN_RIGHT);
		fcf.setHorizontalAlignment(2,0,HasHorizontalAlignment.ALIGN_RIGHT);
		
		loginTable.setWidget(1, 1, user);
		loginTable.setWidget(2, 1, password);
		
		loginTable.setWidget(3, 0, login);
		fcf.setColSpan(3,0,2);
		fcf.setHorizontalAlignment(3,0,HasHorizontalAlignment.ALIGN_CENTER);
				
		DecoratorPanel loginCaption = new DecoratorPanel();

		loginCaption.setWidget(loginTable);

		main.setSize("100%", "100%");
		main.setWidget(0, 0, loginCaption);
		main.getFlexCellFormatter().setHorizontalAlignment(0, 0, HasHorizontalAlignment.ALIGN_CENTER);
		main.getFlexCellFormatter().setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_MIDDLE);
		
		//create login button and add click handler to it
		login.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				//do nothing if the username and password are not filled out
            	if(!user.getText().equalsIgnoreCase("") && !password.getText().equalsIgnoreCase("")){
            		login(user.getText(), password.getText());
            	}
			}		
		});
		
		//add key up handler to the two text boxes
		password.addKeyUpHandler(new KeyUpHandler(){
			@Override
			public void onKeyUp(KeyUpEvent event) {
	            if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
	            	if(!user.getText().equalsIgnoreCase("") && !password.getText().equalsIgnoreCase("")){
	            		login(user.getText(), password.getText());
	            	}
	            }
				
			}
			
		});
		user.addKeyUpHandler(new KeyUpHandler(){
			@Override
			public void onKeyUp(KeyUpEvent event) {
	            if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
	            	if(!user.getText().equalsIgnoreCase("") && !password.getText().equalsIgnoreCase("")){
	            		login(user.getText(), password.getText());
	            	}
	            }
			}
		});
	
		//add main LayoutPanel to the rootpanel
		RootPanel.get().add(main);
	}
	
	/**
	 * This method is used to check if the Http sessionId is still valid
	 * @param sessionID
	 */
	protected void checkHttpSession(String sessionID) {
		//call server to check sessionID
		RPCService.Util.getInstance().checkHttpSession(sessionID, new AsyncCallback<Boolean>(){
			@Override
			public void onFailure(Throwable caught) {
				MessageDialog.showMessageDialog("Error checkHttpSession");
			}
			@Override
			public void onSuccess(Boolean result) {
				//if sessionId is valid the get the User Data that is stored
				//else prompt user to log in
				if(result){
					getUserData();
				} else {
					showLogin();	
				}
			}
		});
		
	}
	
	/**
	 * This function is used to load saved userdata from session
	 */
	protected void getUserData() {
		RPCService.Util.getInstance().getUserData(new AsyncCallback<String>(){
			@Override
			public void onFailure(Throwable caught) {
				MessageDialog.showMessageDialog("Error getUserData");
			}
			@Override
			public void onSuccess(String resultString) {
				//if result is not valid show dialog message else load the main app page
				if(!resultString.equalsIgnoreCase(RPCService.invalidReturnString)){
					UserInfo userInfo = JSONUtil.getUserInfo(resultString);
					onLoginLoad(userInfo);					
				} else {
					MessageDialog.showMessageDialog("Error getUserData");
	
				}
			}
		});
	}

	/**
	 * This method is used to log the user in
	 * @param user
	 * @param password
	 */
	protected void login(String user, String password) {
		//check that user name is valid
		RPCService.Util.getInstance().checkUser(user, new AsyncCallback<String>(){
			@Override
			public void onFailure(Throwable caught) {
				MessageDialog.showMessageDialog("Error login :\n" + caught.getMessage());
			}
			@Override
			public void onSuccess(String resultString) {
				//check that the user is a valid user if so the get the current SessionID else show dialog
				if(!resultString.equalsIgnoreCase(RPCService.invalidReturnString)){
					//ensure that we notify any interested listeners
					getSessionID(resultString);
				} else {
					MessageDialog.showMessageDialog("Error login : " + resultString);
				}
			}
		});
	}
	
	/**
	 * This method is used to get the sessionId and use it to set a cookie
	 * @param userInfo
	 */
	protected void getSessionID(final String userInfo){
		//call server to get current http sessionId
		RPCService.Util.getInstance().getHttpSession(new AsyncCallback<String>() {
			public void onSuccess(String sessionID) {
				//if we get a sessionId then create a cookie, once it is set the set User Data in the session
			    //set cookie to be good for 1 day
			    final long DURATION = 1000 * 60 * 60 * 24; 
			    Date expires = new Date(System.currentTimeMillis() + DURATION);
			    Cookies.setCookie(COOKIE_ID, sessionID, expires, null, "/", false);
			    
				setUserData(userInfo);
			}
			public void onFailure(Throwable caught) {
				MessageDialog.showMessageDialog("Error getSessionID");
			}
		});
	}
	
	/**
	 * This function is used to set the userdata in the http session
	 * @param user
	 */
	protected void setUserData(final String user) {
		//call server and set the user data if successful the turn data into a UserInfo Object and lod main page
		RPCService.Util.getInstance().setUserData(user, new AsyncCallback<Boolean>() {
			public void onSuccess(Boolean returned) {
				if(returned) {
					UserInfo userInfo = JSONUtil.getUserInfo(user);
					onLoginLoad(userInfo);
				} else {
					MessageDialog.showMessageDialog("Error setUserData");
				}
			}
			
			public void onFailure(Throwable caught) {
				MessageDialog.showMessageDialog("Error setUserData");
			}
		});
	}
	
	/**
	 * This function is used to load the app page
	 * @param userInfo
	 */
	protected void onLoginLoad(UserInfo userInfo ) {
    	
		//set browser Title
    	RootPanel.get().clear();		
		//set browser Title
		Window.setTitle("AIC-SMF GWT GUI Version 2");
		final QueryDataManager queryDataManager = new QueryDataManager();
		final EvidenceDataManager evidenceDataManager = new EvidenceDataManager();
		final UserManager userManager = new UserManager();
		final ModelManager modelManager = new ModelManager();
		final QueryOptionsManager queryOptionsManager = new QueryOptionsManager();
		final ExecutionEngineDistributor executionEngineDistributor = new ExecutionEngineDistributor();
		final AICManager aicManager = new AICManager(userManager, modelManager, queryDataManager, evidenceDataManager, queryOptionsManager, executionEngineDistributor);
		
		//link the model and user managers, so that the model is reset when the user changes
		userManager.addUserListener(new UserListener() {
			@Override
			public void loggedInUserChanged(UserInfo newUser) {
				modelManager.setActiveModel(null, false);
			}
		});
		userManager.setLogIn(userInfo);
		
		Toolkit toolkit = Toolkit.buildToolkit(aicManager, evidenceDataManager, executionEngineDistributor, modelManager, queryDataManager, queryOptionsManager, userManager);
        ClientFactory clientFactory = new ClientFactoryImpl(toolkit);
        EventBus eventBus = clientFactory.getEventBus();
        PlaceController placeController = clientFactory.getPlaceController();
        // Start ActivityManager for the main widget with our ActivityMapper
        ActivityMapper activityMapper = new AppActivityMapper(clientFactory);
        ActivityManager activityManager = new ActivityManager(activityMapper, eventBus);
        activityManager.setDisplay(appWidget);

        // Start PlaceHistoryHandler with our PlaceHistoryMapper
        AppPlaceHistoryMapper historyMapper= GWT.create(AppPlaceHistoryMapper.class);
        PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(historyMapper);
        clientFactory.setPlaceHistoryMapper(historyMapper);
        historyHandler.register(placeController, eventBus, defaultPlace);
        appWidget.setSize("100%", "100%");
        
        RootPanel.get().add(appWidget);
        // Goes to the place represented on URL else default place
        historyHandler.handleCurrentHistory();		    
	}
}
